﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using DoeLibsMVC.Models;
using DoeObjects;
using DoeLibsMVC.Filter;

namespace DoeLibsMVC.Controllers.api
{
    public class TitleController : BaseApiController
    {
        /// <summary>
        /// returns a list of all titles
        /// </summary>
        /// <returns></returns>
        public IEnumerable<TitleModel> Get()
        {
            List<Title> allTitles = Title.getAll();
            List<TitleModel> titleModels = new List<TitleModel>();

            foreach (Title title in allTitles)
            {
                TitleModel model = new TitleModel();
                model.Title = title;
                model.Loanables = Loanable.getLoanablesByTitle(title.TitleId);
                model.Authors = Author.getAuthorsFromBook(title.TitleId);
                model.Editors = Author.getEditorsFromBook(title.TitleId);
                model.Topics = Topic.getTopicsFromBook(title.TitleId);

                titleModels.Add(model);
            }

            return titleModels;
        }

        /// <summary>
        /// returns a list of all titles which matches the searchterm and are in at least of one of the given topics
        /// </summary>
        /// <param name="q"></param>
        /// <param name="topics"></param>
        /// <param name="onlyTopicMatches">if set to false it returns only</param>
        /// <returns></returns>
        public HttpResponseMessage Get(string q, string topics, bool onlyTopicMatches = true) 
        {
            string[] topicArray = topics.Split(',');
            List<Topic> topicList = new List<Topic>();
            foreach (string topicname in topicArray)
            {
                List<Topic> topicFound = Topic.getByName(topicname, false);
                if (topicFound.Count > 0)
                {
                    topicList.Add(topicFound[0]);
                }
                else
                {
                    return Request.CreateErrorResponse(HttpStatusCode.NotFound, "Topic \""+topicname+"\" not found");
                }
            }

            List<Title> result = Title.getTitlesRankedFromTopics(q, topicList, onlyTopicMatches);

            return Request.CreateResponse<List<Title>>(HttpStatusCode.OK, result);
        }

        /// <summary>
        /// returns the title with the given ISBN
        /// </summary>
        /// <param name="val">type of ISBN (isbn10 / isbn13)</param>
        /// <param name="id">the isbn number</param>
        /// <returns></returns>
        public HttpResponseMessage Get(string val, string id)
        {
            if (val.ToLower() == "isbn10" || val.ToLower() == "isbn13")
            {
                Title title = null;
                if (val.ToLower() == "isbn10")
                {
                    title = Title.getByIsbn10(id);
                }
                else// if (val.ToLower() == "isbn13")
                {
                    title = Title.getByIsbn13(id);
                }

                if (title != null)
                {
                    return Request.CreateResponse<Title>(HttpStatusCode.OK, title);
                }
                else
                {
                    return Request.CreateResponse(HttpStatusCode.NotFound);
                }
            }
            else
            {
                return Request.CreateErrorResponse(HttpStatusCode.NotFound, val + " is an unsupported ISBN type");
            }
        }

        /// <summary>
        /// returns all titles which the user has loanables for
        /// </summary>
        /// <param name="onlyMine"></param>
        /// <returns></returns>
        [Security.BasicAuthorize(Roles = Security.UserCategoryHelper.STAFF_CATEGORY)]
        public HttpResponseMessage Get(string onlyMine)
        {
            return Request.CreateResponse<IEnumerable<Title>>(HttpStatusCode.OK, Title.getByOwner(User.UserId));
        }

        /// <summary>
        /// returns information about a specific title
        /// </summary>
        /// <param name="id">the titles titleId</param>
        /// <returns></returns>
        public HttpResponseMessage Get(int id)
        {
            Title title = Title.getById(id);
            if (title == null)
            {
                return Request.CreateResponse(HttpStatusCode.NotFound);
            }
            else
            {
                TitleModel model = new TitleModel();
                model.Title = title;
                model.Loanables = Loanable.getLoanablesByTitle(title.TitleId);
                model.Authors = Author.getAuthorsFromBook(title.TitleId);
                model.Editors = Author.getEditorsFromBook(title.TitleId);
                model.Topics = Topic.getTopicsFromBook(title.TitleId);

                return Request.CreateResponse<TitleModel>(HttpStatusCode.OK, model);
            }
        }

        /// <summary>
        /// adds a loanable to the title
        /// </summary>
        /// <param name="id">the titleId</param>
        /// <param name="model">the loananable</param>
        /// <returns></returns>
        [DoeLibsMVC.Filter.ValidateModel]
        [Security.BasicAuthorize(Roles = Security.UserCategoryHelper.STAFF_CATEGORY)]
        public HttpResponseMessage Post(int id, EditLoanableBasicModel model)
        {
            Title title = Title.getById(id);

            if (title != null)
            {
                if (!string.IsNullOrEmpty(model.Barcode) && Loanable.getLoanablesByDoelibsIdentifier(model.Barcode) != null)
                {
                    ModelState.AddModelError("Barcode", "This DoeLibS identity is already in use");
                }

                if (ModelState.IsValid)
                {
                    //Create the loanable and link it
                    Loanable loanable = new Loanable();

                    loanable.Barcode = model.Barcode;
                    loanable.Category = model.CategoryObject;
                    loanable.Location = model.Location;
                    loanable.Owner = this.User;
                    loanable.Title = title;

                    if (loanable.save())
                    {
                        HttpResponseMessage response = Request.CreateResponse<Loanable>(HttpStatusCode.Created, loanable);
                        response.Headers.Location = new Uri(Url.Link("DefaultApi", new { id = loanable.LoanableId }));

                        return response;
                    }
                    else
                    {
                        return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, "Loanable could not be added");
                    }
                }
                else
                {
                    return Request.CreateResponse(HttpStatusCode.BadRequest, FilterHelper.makeErrorsNice(ModelState));
                }
            }
            else
            {
                return Request.CreateResponse(HttpStatusCode.NotFound);
            }

        }

        /// <summary>
        /// create
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        [DoeLibsMVC.Filter.ValidateModel]
        [Security.BasicAuthorize(Roles = Security.UserCategoryHelper.STAFF_CATEGORY)]
        public HttpResponseMessage Post(EditTitleModel model)
        {
            if (string.IsNullOrEmpty(model.ISBN10) && string.IsNullOrEmpty(model.ISBN13))
            {
                ModelState.AddModelError("ISBN10", "At least one ISBN number must be specified.");
                ModelState.AddModelError("ISBN13", "At least one ISBN number must be specified.");
            }

            //check that the ISBNs not in use or only in use for the title which is edited...
            Title titleWithIsbn10 = Title.getByIsbn10(model.ISBN10);
            Title titleWithIsbn13 = Title.getByIsbn13(model.ISBN13);

            if (!string.IsNullOrEmpty(model.ISBN10) && titleWithIsbn10 != null)
            {
                ModelState.AddModelError("ISBN10", "This ISBN is already in use by another title.");
            }
            if (!string.IsNullOrEmpty(model.ISBN13) && titleWithIsbn13 != null)
            {
                ModelState.AddModelError("ISBN13", "This ISBN is already in use by another title.");
            }

            if (ModelState.IsValid)
            {
                Title title = new Title();
                title.ISBN10 = model.ISBN10;
                title.ISBN13 = model.ISBN13;
                title.BookTitle = model.Title;
                title.EditionNumber = model.Edition;
                title.EditionYear = model.PublicationYear;
                title.FirstEditionYear = model.FirstEditionYear;
                title.Publisher = model.PublisherObject;

                if (title.Publisher.PublisherId > 0)
                {
                    if (title.save())
                    {
                        //after title is saved, the authors can be referenced
                        foreach (Author a in model.AuthorList)
                        {
                            Title.addAuthor(a.Aid, title.TitleId, false);
                        }
                        //reference the editors
                        foreach (Author editor in model.EditorList)
                        {
                            Title.addAuthor(editor.Aid, title.TitleId, true);
                        }

                        foreach (Topic t in model.TopicList)
                        {
                            Title.addTopic(t.TopicId, title.TitleId);
                        }

                        //HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.Created);
                        //response.Headers.Location = new Uri(Url.Link("DefaultApi", new { id = title.TitleId }));

                        //response.Content = new ObjectContent<Title>(title, new System.Net.Http.Formatting.JsonMediaTypeFormatter());

                        return Request.CreateResponse<Title>(HttpStatusCode.Created, title);
                        //return new HttpResponseMessage(HttpStatusCode.OK);
                    }
                    else
                    {
                        //base.Message = new HttpMessage("Technical problems during save. Please try again.", false);
                        return Request.CreateResponse(HttpStatusCode.InternalServerError, "Technical problems during save. Please try again");
                    }
                }
                else
                {
                    //base.Message = new HttpMessage("Publisher could not be added.", false);
                    return Request.CreateResponse(HttpStatusCode.InternalServerError, "Publisher could not be added");
                }
            }
            else
            {
                return Request.CreateResponse(HttpStatusCode.BadRequest, Filter.FilterHelper.makeErrorsNice(ModelState));
            }
        }

        /// <summary>
        /// update
        /// </summary>
        /// <param name="model"></param>
        /// <param name="id"></param>
        /// <returns></returns>
        [DoeLibsMVC.Filter.ValidateModel]
        [Security.BasicAuthorize(Roles = Security.UserCategoryHelper.STAFF_CATEGORY)]
        public HttpResponseMessage Put(EditTitleModel model, int id)
        {
            Title title = Title.getById(id);

            if (title == null)
            {
                return Request.CreateErrorResponse(HttpStatusCode.NotFound, "No title with Id " + id + " found");
            }

            if (string.IsNullOrEmpty(model.ISBN10) && string.IsNullOrEmpty(model.ISBN13))
            {
                ModelState.AddModelError("ISBN10", "At least one ISBN number must be specified.");
                ModelState.AddModelError("ISBN13", "At least one ISBN number must be specified.");
            }

            //check that the ISBNs not in use or only in use for the title which is edited...
            Title titleWithIsbn10 = Title.getByIsbn10(model.ISBN10);
            Title titleWithIsbn13 = Title.getByIsbn13(model.ISBN13);

            if (!string.IsNullOrEmpty(model.ISBN10) && titleWithIsbn10 != null && titleWithIsbn10.TitleId != id)
            {
                ModelState.AddModelError("ISBN10", "This ISBN is already in use by another title.");
            }
            if (!string.IsNullOrEmpty(model.ISBN13) && titleWithIsbn13 != null && titleWithIsbn13.TitleId != id)
            {
                ModelState.AddModelError("ISBN13", "This ISBN is already in use by another title.");
            }



            if (ModelState.IsValid)
            {
                title.ISBN10 = model.ISBN10;
                title.ISBN13 = model.ISBN13;
                title.BookTitle = model.Title;
                title.EditionNumber = model.Edition;
                title.EditionYear = model.PublicationYear;
                title.FirstEditionYear = model.FirstEditionYear;
                title.Publisher = model.PublisherObject;

                //set the authors:
                //remove all first
                Title.deleteAllAuthorTitleReferences(id);
                //add all
                foreach (Author a in model.AuthorList)
                    Title.addAuthor(a.Aid, id, false);

                //add all editors
                foreach (Author editor in model.EditorList)
                    Title.addAuthor(editor.Aid, id, true);

                Title.deleteAllTopicTitleReferences(id);
                foreach (Topic t in model.TopicList)
                    Title.addTopic(t.TopicId, id);

                if (title.Publisher.PublisherId > 0)
                {
                    if (title.save())
                    {
                        return Request.CreateResponse(HttpStatusCode.OK);
                    }
                    else
                    {
                        //base.Message = new HttpMessage("Technical problems during save. Please try again.", false);
                        return Request.CreateResponse(HttpStatusCode.InternalServerError, "Technical problems during save. Please try again");
                    }
                }
                else
                {
                    //base.Message = new HttpMessage("Publisher could not be added.", false);
                    return Request.CreateResponse(HttpStatusCode.InternalServerError, "Publisher could not be added");
                }
            }
            else
            {
                return Request.CreateResponse(HttpStatusCode.BadRequest, Filter.FilterHelper.makeErrorsNice(ModelState));
            }
        }
    }
}
